﻿
Imports System.Threading
Imports std
Imports lock_t = lock(Of locks.monitorlock)

Public Class callbackManager2
    Implements icallbackManager
    Public Shared ReadOnly [global] As callbackManager2 = Nothing
    Private Shared ReadOnly CALLBACKMANAGER_CHECK_QUEUE_LENGTH As Int64
    Private Shared ReadOnly CALLBACKMANAGER_CHECK_QUEUE_TICKS As Int64
    Private Shared ReadOnly CALLBACKMANAGER_CHECK_QUEUE_INTERVAL_TICKS As Int64
    Private Shared ReadOnly CALLBACKMANAGER_CHECK_QUEUE_UNCHANGED_CYCLE As Int64
    Private ReadOnly q As list(Of callbackAction) = Nothing
    Private ReadOnly are As AutoResetEvent = Nothing
    Private lock As lock_t

    Shared Sub New()
        CALLBACKMANAGER_CHECK_QUEUE_LENGTH =
            counter.registerAverageAndLastAverage("CALLBACKMANAGER2_CHECK_QUEUE_LENGTH")
        CALLBACKMANAGER_CHECK_QUEUE_TICKS =
            counter.registerAverageAndLastAverage("CALLBACKMANAGER2_CHECK_QUEUE_TICKS")
        CALLBACKMANAGER_CHECK_QUEUE_INTERVAL_TICKS =
            counter.registerAverageAndLastAverage("CALLBACKMANAGER2_CHECK_QUEUE_INTERVAL_TICKS")
        CALLBACKMANAGER_CHECK_QUEUE_UNCHANGED_CYCLE =
            counter.registerAverageAndLastAverage("CALLBACKMANAGER2_CHECK_QUEUE_UNCHANGED_CYCLE")
        [global] = New callbackManager2()
    End Sub

    Public Shared Sub register(ByVal resolver As resolver)
        resolver.register(Of icallbackManager)([global])
    End Sub

    Public Shared Sub register()
        register(resolver.global)
    End Sub

    Private tp As threadpool = Nothing

    Public Sub New(ByVal tp As threadpool)
        assert(Not tp Is Nothing)
        tp.threadCount() = Environment.ProcessorCount() + 1
        For i As Int64 = 0 To Environment.ProcessorCount() - 1
            tp.insertJob(AddressOf check_queue, Nothing)
        Next
        Me.tp = tp
        q = New list(Of callbackAction)()
        are = New AutoResetEvent(False)
    End Sub

    Public Sub New()
        Me.New(resolve(Of ithreadpool)())
    End Sub

    Private Sub trigger_check() Implements icallbackManager.trigger_check
        are.Set()
    End Sub

    Private Sub lock_q(ByVal d As void)
        lock.wait()
        d()
        lock.release()
    End Sub

    Private Function action(ByVal c As callbackAction, ByVal pushback As Boolean,
                            Optional ByVal endingOper As void = Nothing) As Boolean
        assert(Not c Is Nothing)
        Dim rtn As Boolean = True
        If c.begining() Then
            c.manager() = Me
            c.action_begin()
        End If
        If c.pending() Then
            c.action_check()
            If c.pending() Then
                lock_q(Sub()
                           If pushback Then
                               rtn = q.push_back(c)
                           Else
                               rtn = q.push_front(c)
                           End If
                       End Sub)
            End If
        End If
        If c.ending() Then
            c.action_end()
            assert(c.finished())
            If Not endingOper Is Nothing Then
                endingOper()
            End If
            trigger_check()
        End If

        Return rtn
    End Function

    Private Sub check_queue()
        Dim unchangedCycle As Int64 = 0
        While True
            counter.recordTimeBegin()
            Dim len As Int64 = 0
            lock_q(Sub()
                       len = q.size()
                   End Sub)
            counter.increase(CALLBACKMANAGER_CHECK_QUEUE_LENGTH, len)
            Dim changed As Int64 = 0
            Dim i As Int64 = 0
            While i < len AndAlso Not tp.stopping()
                Dim c As callbackAction = Nothing
                lock_q(Sub()
                           c = q.back()
                           If Not c Is Nothing Then
                               assert(q.pop_back())
                           End If
                       End Sub)
                If c Is Nothing OrElse
                   (Not debugassert(action(c,
                                           False,
                                           Sub()
                                               changed += 1
                                           End Sub))) Then
                    Exit While
                End If
                i += 1
            End While
            counter.recordTimeTicks(CALLBACKMANAGER_CHECK_QUEUE_TICKS)
            If changed > 0 OrElse tp.stopping() Then
                counter.increase(CALLBACKMANAGER_CHECK_QUEUE_UNCHANGED_CYCLE, unchangedCycle)
                unchangedCycle = 0
            Else
                unchangedCycle += 1
            End If
            If tp.stopping() Then
                Exit While
            End If
            counter.recordTimeBegin()
            yield_wait(are)
            counter.recordTimeTicks(CALLBACKMANAGER_CHECK_QUEUE_INTERVAL_TICKS)
        End While
    End Sub

    Private Shared Function current_pending_timeout() As Boolean
        Return Not callbackAction.getCurrent() Is Nothing AndAlso
               callbackAction.getCurrent().pending() AndAlso
               callbackAction.getCurrent().timeout()
    End Function

    Public Function begin(ByVal c As callbackAction) As Boolean Implements icallbackManager.begin
        If c Is Nothing OrElse current_pending_timeout() Then
            Return False
        Else
            Return action(c, True)
        End If
    End Function

    Public Function Clone() As Object Implements ICloneable.Clone
        Return Me
    End Function
End Class
